/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package task

import (
	"time"
	"sync"
)

type timeArg struct {
	Time *time.Time
	Arg interface{}
}

type timeArgs map[string]*timeArg

type ExpireContainer struct {
	args timeArgs
	prev *time.Time
	now *time.Time
	close chan struct{}
	locker sync.Mutex
}

func NewExpireContainer(d time.Duration) (c *ExpireContainer, expired <-chan map[string]interface{}) {
	now := time.Now()
	c = &ExpireContainer{args: make(timeArgs, 10), now: &now, close: make(chan struct{})}
	exps := make(chan map[string]interface{}, 3)
	go func() {
		ticker := time.NewTicker(d)
		for {
			select {
			case now := <-ticker.C:
				func() {
					c.locker.Lock()
					defer c.locker.Unlock()
					if c.prev != nil {
						args := make(map[string]interface{}, 200)
						for id, arg := range c.args {
							// 过期数据
							if arg.Time == c.prev {
								args[id] = arg.Arg
								// 删除过期数据
								delete(c.args, id)
							}
						}
						if len(args) > 0 {
							exps <- args
						}
					}
					c.prev = c.now
					c.now = &now
				}()
			case <- c.close:
				c.locker.Lock()
				exps<-nil
				c.locker.Unlock()
				return
			}
		}
	}()
	return c, exps
}

// 关闭服务, 未过期的数据将被抛弃
func (c *ExpireContainer) Close() {
	c.close <- struct{}{}
}

func (c *ExpireContainer) Add(id string, arg interface{}) {
	c.locker.Lock()
	defer c.locker.Unlock()
	if ta := c.args[id]; ta != nil {
		ta.Time = c.now
		ta.Arg = arg
	} else {
		c.args[id] = &timeArg{Time: c.now, Arg: arg}
	}
}

func (c *ExpireContainer) Delete(name string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.args, name)
}
